package easycloud.ecs.provider.snda;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.snda.storage.security.SNDACredentials;
import com.snda.storage.service.CSService;
import com.snda.storage.service.impl.rest.httpclient.RestCSService;
import com.snda.storage.service.model.CSBucket;
import com.snda.storage.service.model.CSObject;
import com.snda.storage.service.model.StorageObject;

import easycloud.ecs.api.IProvider;
import easycloud.ecs.api.IPutResult;
import easycloud.ecs.api.ProviderException;
import easycloud.ecs.api.model.IStorageObject;
import easycloud.ecs.api.model.IStorageObjectListing;
import easycloud.ecs.api.model.IStorageObjectMetadata;
import easycloud.ecs.config.ConfigException;
import easycloud.ecs.config.ProviderConfig;
import easycloud.ecs.config.ProviderConfigUtils;
import easycloud.ecs.config.ProviderStatistics;
import easycloud.ecs.provider.snda.model.SndaStorageObject;
import easycloud.ecs.provider.snda.model.SndaStorageObjectListing;
import easycloud.ecs.provider.snda.model.SndaStorageObjectMetadata;

public class SndaCloudStorageProvider implements IProvider {

	private ProviderConfig config;

	private ProviderStatistics statistics;

	private static CSService adapter;

	private boolean isReady = false;

	private static final Log logger = LogFactory
			.getLog(SndaCloudStorageProvider.class);

	private static String BUCKET_NAME;

	private static final int RETRY_TIMES = 3;

	public SndaCloudStorageProvider(ProviderConfig config) {
		this.config = config;
		statistics = new ProviderStatistics();
		SndaCloudStorageProvider.adapter = new RestCSService(
				new SNDACredentials(this.config.getAccessKey(),
						this.config.getSecretKey()));
		SndaCloudStorageProvider.BUCKET_NAME = this.config.getBucketName();
	}

	/**
	 * 
	 */
	@Override
	public boolean delete(String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		try {
			statistics.incNumberOfDelete();
			SndaCloudStorageProvider.adapter.deleteObject(
					SndaCloudStorageProvider.BUCKET_NAME, key);
			result = true;
			SndaCloudStorageProvider.logger.info(getName() + " delete OK: "
					+ key);
		} catch (Exception e) {
			SndaCloudStorageProvider.logger.warn(getName() + " delete failed",
					e);
			throw new ProviderException(getName() + " delete failed", e, key);
		}
		return result;
	}

	/**
	 * 
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObject getFile(String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		StorageObject result = null;
		try {
			statistics.incNumberOfGet();
			result = SndaCloudStorageProvider.adapter.getObject(
					SndaCloudStorageProvider.BUCKET_NAME, key);
			SndaCloudStorageProvider.logger.info(getName() + " getFile OK: "
					+ key);
		} catch (Exception e) {
			SndaCloudStorageProvider.logger.warn(getName() + " getFile failed",
					e);
			throw new ProviderException(getName() + " getFile failed", e, key);
		}
		if (result != null) {
			return new SndaStorageObject(result);
		} else {
			return null;
		}
	}

	@Override
	public String getName() {
		return "SndaCloudStorage";
	}

	@Override
	public ProviderStatistics getProviderStatistics() {
		return statistics;
	}

	/**
	 * 
	 */
	@Override
	public IStorageObjectMetadata head(String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		StorageObject result = null;
		try {
			statistics.incNumberOfHead();
			result = SndaCloudStorageProvider.adapter.headObject(
					SndaCloudStorageProvider.BUCKET_NAME, key);
			SndaCloudStorageProvider.logger
					.info(getName() + " head OK: " + key);
		} catch (Exception e) {
			SndaCloudStorageProvider.logger.warn(getName() + " head failed", e);
			throw new ProviderException(getName() + " head failed", e, key);
		}
		if (result != null) {
			return new SndaStorageObjectMetadata(result);
		} else {
			return null;
		}
	}

	/**
	 * 
	 */
	@Override
	public IStorageObjectListing list(String path, String delimiter)
			throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		CSObject[] result = null;
		try {
			statistics.incNumberOfList();
			result = SndaCloudStorageProvider.adapter.listObjects(
					SndaCloudStorageProvider.BUCKET_NAME, path, "/");
			SndaCloudStorageProvider.logger.info(getName() + " list OK: "
					+ path);
		} catch (Exception e) {
			SndaCloudStorageProvider.logger.warn(getName() + " list failed", e);
			throw new ProviderException(getName() + " list failed", e, path);
		}
		if (result != null) {
			return new SndaStorageObjectListing(result);
		} else {
			return null;
		}
	}

	/**
	 * 
	 */
	@Override
	public List<String> listRoots() throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		Set<String> result;
		try {
			statistics.incNumberOfListRoots();
			CSObject[] objects = SndaCloudStorageProvider.adapter.listObjects(
					SndaCloudStorageProvider.BUCKET_NAME, "", "/");
			result = new HashSet<String>();
			for (CSObject object : objects) {
				if (object.getName().indexOf("/") != -1) {
					String dirName = object.getName().substring(0,
							object.getName().indexOf("/") + 1);
					result.add(dirName);
				}
			}
			SndaCloudStorageProvider.logger.info(getName() + " listRoots OK");
		} catch (Exception e) {
			SndaCloudStorageProvider.logger.warn(getName()
					+ " listRoots failed", e);
			throw new ProviderException(getName() + " listRoots failed", e, "");
		}
		return new ArrayList<String>(result);
	}

	/**
	 * 
	 * @param file
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IPutResult putFile(File file, String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		StorageObject storageObject = null;
		StorageObject putResult = null;
		if (file.isFile() && (file.length() > 0)) {
			storageObject = new CSObject(key);
			storageObject.setDataInputFile(file);
			storageObject.setContentLength(file.length());
			statistics.incNumberOfPut();
			putResult = SndaCloudStorageProvider.adapter.putObject(
					SndaCloudStorageProvider.BUCKET_NAME, storageObject);
			SndaCloudStorageProvider.logger.info(getName() + " putFile OK: "
					+ key);
		}
		return new SndaPutResult(putResult);
	}

	@Override
	public boolean ready() {
		if (isReady) {
			return true;
		}

		boolean result = false;
		try {
			if (StringUtils.isEmpty(SndaCloudStorageProvider.BUCKET_NAME)) {
				SndaCloudStorageProvider.BUCKET_NAME = UUID.randomUUID()
						.toString();
				config.setBucketName(SndaCloudStorageProvider.BUCKET_NAME);
				SndaCloudStorageProvider.adapter
						.createBucket(SndaCloudStorageProvider.BUCKET_NAME);
				ProviderConfigUtils.saveProviderConifg(config);
			}

			int retry = 0;
			while (!result && (retry++ < SndaCloudStorageProvider.RETRY_TIMES)) {
				try {
					CSBucket bucket = SndaCloudStorageProvider.adapter
							.getOrCreateBucket(SndaCloudStorageProvider.BUCKET_NAME);
					result = bucket != null;
					if (result) {
						isReady = true;
						break;
					}
				} catch (Exception e) {
					SndaCloudStorageProvider.logger.warn(getName()
							+ " query bucket failed", e);
				}

				SndaCloudStorageProvider.BUCKET_NAME = UUID.randomUUID()
						.toString();
				config.setBucketName(SndaCloudStorageProvider.BUCKET_NAME);
				SndaCloudStorageProvider.adapter
						.createBucket(SndaCloudStorageProvider.BUCKET_NAME);
				ProviderConfigUtils.saveProviderConifg(config);
			}
		} catch (ConfigException e) {
			SndaCloudStorageProvider.logger.warn(getName() + " init failed", e);
			result = false;
		}
		return result;
	}

	@Override
	public void setProviderStatistics(ProviderStatistics stat) {
		statistics = stat;
	}

}
